/* formident.c */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "kernel.h"
#include "newfs.h"
#include "swis.h"

/***************************************************************************/

_kernel_oserror *display_format_help(void)

  /* This routine must write out appropriate line(s) of "help" information
      about possible NewFS formats.

     It is called in response to the user typing "*help format".

     See PRM page 2-278.
  */

{
  _kernel_oserror *err;

  debug("Service_DisplayFormatHelp\n");

  err = _swix(OS_Write0, I0,
              "NewFS/A - trivial IFS exemplar format"
             );
  if (err == NULL)
    err = _swix(OS_NewLine, 0);

  return err;
}

/***************************************************************************/

BOOL identify_format(char *format_id, int *format_arg, int *layout_arg)

  /* Called to identify a format string.

     If format_id is one recognised by NewFS, then return TRUE; otherwise
      return FALSE.

     If TRUE, then *format_arg will be passed to the format-negotiating
      routine, and *layout_arg will be passed to the layout routine.

     See PRM page 2-277.
  */

{
  debug("Service_IdentifyFormat: '%s'\n", format_id);

 /* we only recognise "NewFS/A" at present */
  if (strcmp(format_id, "NewFS/A") == 0)
  {
    *format_arg = 'A';
    *layout_arg = 'A';

    return TRUE;
  }

  return FALSE;
}

/***************************************************************************/

_kernel_oserror *add_format_info(format_spec **list)

  /* Called to add information about NewFS formats when the user selects the
      "format" menu.

     See PRM page 2-494 (but note that SWI NewFS_DiscFormat is called as
      soon as the "Format" submenu arrow is traversed - regardless of whether
      the NewFS/A format is selected).

  */

{
  format_spec *fspec;
  char *menu = "NewFS/A";
  char *help = "NewFS/A - trivial IFS exemplar format";
  format_spec **p;

  debug("Service_EnumerateFormats\n");

 /* see PRM page 2-495! */
  fspec = (format_spec*)check_alloc(sizeof(format_spec));

  fspec->submenu_text = NULL;
  fspec->help_text = NULL;

 /* add this format specification to the end of the list */
  p = list;

  while (*p != NULL)
    p = &((*p)->next);

  fspec->next = NULL;
  *p = fspec;

 /* fill in the remaining fields */
  fspec->submenu_text = (char*)check_alloc(strlen(menu)+1);
  fspec->help_text = (char*)check_alloc(strlen(help)+1);
  strcpy(fspec->submenu_text, menu);
  strcpy(fspec->help_text, help);

  fspec->format_swi_number = NEWFS_SWI_BASE + SWI_NEWFS_DISCFORMAT;
  fspec->format_arg = 'A';

  fspec->layout_swi_number = NEWFS_SWI_BASE + SWI_NEWFS_LAYOUTSTRUCTURE;
  fspec->layout_arg = 'A';

  fspec->flags = 0;

  return NULL;
}

/***************************************************************************/

_kernel_oserror *NewFS_DiscFormat(_kernel_swi_regs *r, void *private)

  /* Called to negotiate a format.

     See PRM pages 2-329, 2-325 - and note above in add_format_info(..).
  */

{
  format_str *f = (format_str*)(r->r[0]);
  _kernel_oserror *err;

  debug("NewFS_DiscFormat (SWI): format_param = %d\n", r->r[3]);

 /* set "ideal" physical format to be that of an E-format disc:
     for details, see source file Asm.FormSWIs inside FileCore module */
  f->sector_size = 1024;
  f->gap1_side0 = 32+271;
  f->gap1_side1 = 32+0;
  f->gap3 = 90;
  f->sectors_per_track = 5;
  f->density = 2;
  f->options = 0;
  f->start_sector_number = 0;
  f->interleave = 0;
  f->side_skew = 0;
  f->track_skew = 0;
  f->fill_value = 0xa5;
  f->tracks = 160;

 /* don't forget the reserved bytes which must be zero */
  {
    int i;

    for (i=0; i<36; i++)
      f->reserved[i] = 0;
  }

 /* call vetting SWI */
  err = _swix(r->r[1], I0|I1,
              r->r[0],
              r->r[2]
             );

  if (err != NULL)
    debug("Vetting SWI says '%s'\n", err->errmess);

  return err;
}

/***************************************************************************/

BOOL identify_disc
(
  disc_rec *drec,
  char *curr_format_buff,
  int buff_size,
  int *cache_ptr,
  int private,
  int *image_type
)

  /* Called to identify a newly-mounted disc.

       drec       - addresses a partly-filled disc record descrbing the disc.

       curr_format_buff
                  - addresses a buffer of length buff_size into which a
                     description of the disc's format should be copied if it
                     is recognised - but may be NULL.

       cache_ptr  - addresses the location containing a "sector cache handle"
                     which is passed to FileCore_DiscOp when reading data
                     from the disc. FileCore may change the handle, and so it
                     must be copied back to *cache_ptr before exit.

       private    - is the magic value that must be quoted when calling
                     FileCore_DiscOp.

       image_type - is the address of a location to which the filetype
                     corresponding to the disc image should be written if it
                     is recognised.

     See PRM page 2-321 for details of the identification process, and
      page 2-218 for details of Service_IdentifyDisc - which calls this
      function.
  */

{
  char *buff;
  _kernel_oserror *err;
  int remainder;
  image_hdr *imhdr;

  debug("Service_IdentifyDisc\n");

 /* some checks: there ought to be more */
  if (drec->log2secsize != 10 ||
      drec->secspertrack != 5)
    return FALSE;

 /* seems ok - so set value for disc size */
  drec->disc_size = 800*1024;

 /* now look at the contents of the first sector - should be image header */

 /* allocate a buffer */
  buff = (char *)check_alloc(1024);

 /* read first sector using FileCore_DiscOp 9 */
  err = _swix(FileCore_DiscOp, I1|I2|I3|I4|I6|I8|O4|O6,
              9+64 + (((unsigned)drec >> 2) << 8),
               /* 9 => "read sector via cache"
                 64 => ignore escape
                 top three bytes contain disc record's address */
              drec->root & 0xe0000000,
               /* disc address to read from - drive number is taken from the
                   "root" field in the disc record, byte offset is 0 */      
              buff,           /* read sector into this buffer */
              1024,           /* sector size */
              *cache_ptr,     /* sector cache handle */
              private,        /* FileCore "private" word */
              &remainder,     /* set to number of bytes *not* read */
              cache_ptr
               /* new sector cache handle will be written to this address */
             );

  if (err != NULL)
    debug("Problem with FileCore_DiscOp 9: '%s'\n", err->errmess);

  if (remainder != 0)
    debug("%d bytes left to read of first sector\n", remainder);

 /* check image header */
  imhdr = (image_hdr*)buff;

  if (imhdr->id != NEWFS_ID)
  {
    check_free(buff);
    return FALSE;
  }
  
 /* copy disc name from image header to disc record */
  strncpy(drec->disc_name, imhdr->image_name, 10);     /* max 10 chars */

 /* copy image stamp value from image header to disc record */
  drec->disc_id_lo = imhdr->stamp_value;
  drec->disc_id_hi = imhdr->stamp_value >> 8;

 /* copy boot option from image header to disc record */
  drec->bootoption = imhdr->boot_option;

 /* if "current format" buffer present, copy format description into it */
  if (curr_format_buff != 0)
    strncpy(curr_format_buff, "NewFS A", buff_size);

 /* return image filetype */
  *image_type = IMAGE_TYPE;

 /* free sector buffer */
  check_free(buff);

  return TRUE;
}

/***************************************************************************/
